/*
Copyright 2007 John Tsiombikas <nuclear@siggraph.org>

This file is part of the pixelshow 2007 invitation demo.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <GL/glew.h>
#include "obj.h"
#include "mesh.h"
#include "ggen.h"
#include "sdr.h"
#include "image.h"
#include "duck_mesh.h"
#include "timings.h"

static void draw_sphere(unsigned int msec);
static void draw_ducks(unsigned int msec);
static void draw_design(unsigned int msec);
static void deform_sphere(unsigned int msec);
void startup_text(const char *txt);

static struct mesh *sph;
static unsigned int prog;
static unsigned int cel_pal;
static float cam_rot, cam_pitch = 25;

extern int num_ducks;

int init_objects(void)
{
	int img_x, img_y;
	void *img;

	startup_text("generating geometry...");
	if(!(sph = create_mesh(0, 0)) || gen_sphere(sph, 1.0, 14, 1.0, 1.0)) {
		startup_text("... failed");
		return -1;
	}

	startup_text("loading shaders...");
	if(!(prog = create_program_load("sdr/obj.v.glsl", "sdr/obj.p.glsl"))) {
		startup_text("... failed");
	}

	startup_text("loading textures...");
	if(!(img = load_image("data/cont_cel_pal.png", &img_x, &img_y))) {
		startup_text("... failed");
	}

	glGenTextures(1, &cel_pal);
	glBindTexture(GL_TEXTURE_2D, cel_pal);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, img_x, img_y, 0, GL_BGRA, GL_UNSIGNED_BYTE, img);

	return 0;
}

static void foo(unsigned int msec)
{
	draw_sphere(msec);
	if(msec > TM_S_DUCKS) { 
		draw_ducks(msec);
	}
}

#define MIN(a, b)	((a) < (b) ? (a) : (b))
void draw_objects(unsigned int msec)
{
	extern int xsz, ysz;
	float t = (float)msec / 1000.0f;
	float xoffs = t * (t/4.0) - 4.5;

	deform_sphere(msec);

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluPerspective(45.0, (float)xsz / (float)ysz, 1.0, 1000.0);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glTranslatef(MIN(xoffs, 0.6), 0, -6);
	glRotatef(cam_pitch, 1, 0, 0);
	glRotatef(cam_rot, 0, 1, 0);

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, cel_pal);
	bind_program(prog);
	
	foo(msec);

	bind_program(0);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);

	draw_design(msec);

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}


static void draw_sphere(unsigned int msec)
{
	const float dcol[] = {0.70, 0.83, 1.0, 1.0};
	const float scol[] = {1, 1, 1, 1};

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dcol);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol);
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 60.0);

	draw_mesh(sph);
}

static void draw_ducks(unsigned int msec)
{
	static int dlist = 0;
	const float scale = 0.0005;
	const float dcol[] = {0.8, 0.7, 0.1, 1.0};
	const float scol[] = {1, 1, 1, 1};
	int i, j;

	float t = (float)msec / 80.0f;
	float sec = (float)msec / 1000.0f;

	if(!dlist) {
		dlist = glGenLists(1);
		glNewList(dlist, GL_COMPILE);
		glBegin(GL_TRIANGLES);
		for(i=0; i<DUCK_NFACE; i++) {
			for(j=0; j<3; j++) {
				float *vert = duck_vertices[duck_triangles[i][j]];
				int nidx = i * 3 + j;

				glNormal3fv(duck_normals[nidx]);
				glVertex3f(vert[0] * scale, vert[1] * scale + 1.0, vert[2] * scale + 0.6);
			}
		}
		glEnd();
		glEndList();
	}

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dcol);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol);
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 60.0);

	glMatrixMode(GL_MODELVIEW);
	for(i=0; i<num_ducks; i++) {
		glPushMatrix();
		glRotatef(360.0 * (float)i / (float)num_ducks - t, 0, 1, 0);
		glTranslatef(2.0, -0.7, 0);
		glRotatef(sin(sec * M_PI * 2.0 * 1.75) * 20.0, 1, 0, 0);

		glCallList(dlist);

		glPopMatrix();
	}
}


static void draw_design(unsigned int msec)
{
	const float dur = (float)TM_DUR_FADEIN / 1000.0;
	float scale, t;

	if(msec > TM_DUR_FADEIN) return;

	glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	/* burn stencil */
	glEnable(GL_STENCIL_TEST);
	glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
	glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

	glDepthMask(0);

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	t = (float)msec / 1000.0;
	scale = cos((t / dur) * M_PI / 2.0);
	glScalef(scale * 10.0, scale, 1.0);

	glBegin(GL_QUADS);
	glColor4f(0, 0, 0, 1);
	glVertex2f(-1, -1);
	glVertex2f(1, -1);
	glVertex2f(1, 1);
	glVertex2f(-1, 1);
	glEnd();

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	/* render objects where stencil > 0 */
	glStencilFunc(GL_EQUAL, 1, 0xffffffff);
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

	glDepthMask(1);

	glLineWidth(2);

	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE);
	glColor3f(0.93, 0.08, 0.36);

	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	foo(msec);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	glDisable(GL_BLEND);

	glDisable(GL_STENCIL_TEST);
}



static void deform_sphere(unsigned int msec)
{
	int i;
	static struct vertex *orig_varr;

	float t = (float)msec / 1000.0f;

	if(!orig_varr) {
		orig_varr = malloc(sph->vnum * sizeof *orig_varr);
		memcpy(orig_varr, sph->varr, sph->vnum * sizeof *orig_varr);
	}

	for(i=0; i<sph->vnum; i++) {
		/*float u = orig_varr[i].texc.x * M_PI;*/
		float v = orig_varr[i].texc.y * M_PI;

		float sfact = 0.2 * (sin(v * 2.0 + t * 2.0) + cos(v * 4.0 - t * 4.0) / 2.0);
		sph->varr[i].pos.x = orig_varr[i].pos.x + orig_varr[i].norm.x * sfact;
		sph->varr[i].pos.y = orig_varr[i].pos.y + orig_varr[i].norm.y * sfact;
		sph->varr[i].pos.z = orig_varr[i].pos.z + orig_varr[i].norm.z * sfact;
	}
	make_kdtree(sph);
	calc_normals(sph);
}


/*static int prev_x = -1;
static int prev_y = -1;
static int pbn;


static void mouse_button(int button, int state, int x, int y)
{
	if(state == GLUT_DOWN) {
		prev_x = x;
		prev_y = y;
		pbn = button;
	} else {
		prev_x = -1;
		prev_y = -1;
	}
}


static void mouse_motion(int x, int y)
{
	if(pbn == GLUT_LEFT_BUTTON) {
		cam_rot += (float)(x - prev_x) / 2.0f;
		cam_pitch += (float)(y - prev_y) / 2.0f;
		if(cam_pitch > 90.0) cam_pitch = 90.0;
		if(cam_pitch < -90.0) cam_pitch = -90.0;
		
		prev_x = x;
		prev_y = y;
	}
}*/
